package org.jadapter.tests;

import java.util.LinkedList;
import java.util.List;

import junit.framework.TestCase;

import org.jadapter.func.Function;
import org.jadapter.func.Lambda;

import static org.jadapter.func.Function.F2;
import static org.jadapter.func.Function.F3;
import static org.jadapter.func.Function.curry;
import static org.jadapter.func.Function.find;
import static org.jadapter.func.Function.reduce;
import static org.jadapter.func.Function.remove;


public class TestFunction extends TestCase {
    List<Integer> ints;
    Function<Function<Integer,Integer>,Integer> add;
    
    
    @Override
	@SuppressWarnings("serial")
    public void setUp() {
        ints= new LinkedList<Integer>(){{
            for(int i = 1; i < 6; i++) add(i);
        }};
        
        // close your eyes and skip the next few lines 
        // chaining Function objects to get more generic parameters
        // the result smells like a curry..
        add = new Function<Function<Integer,Integer>,Integer>(){                       
			public Function<Integer,Integer> invoke(final Integer arg0) {
                return new Function<Integer,Integer>(){                    
					public Integer invoke(Integer arg1) {
                        return arg0 + arg1;
                    }                    
                };
            }          
        };        
    }
    
    public void testShouldFilterOutEvenNumbers() {
        assertEquals(5, ints.size());
        
        Lambda<Boolean,Integer> odd = new Lambda<Boolean,Integer>(){
            public Boolean x(Integer a) {
                return a % 2 == 1;
            }          
        };
        
        remove(odd, ints);
        
        assertEquals(3, ints.size());
        assertEquals(1, (int)ints.get(0));
        assertEquals(3, (int)ints.get(1));
        assertEquals(5, (int)ints.get(2));        
    }
    
    public void testFind() {
        
        Lambda<Boolean,Integer> moreThanSix = new Lambda<Boolean,Integer>(){
            public Boolean x(Integer a) {
                return a > 6;
            }          
        };             
        
        assertNull(find(moreThanSix, ints));         
        
        
        Lambda<Boolean,Integer> moreThanThree = new Lambda<Boolean,Integer>(){
            public Boolean x(Integer a) {
                return a > 3;
            }          
        };      
        
        int fourOrMore = find(moreThanThree, ints);
        
        assertTrue(fourOrMore > 3);
       
        
    }

    public void testReduce() {
        int x = add.invoke(1).invoke(2);       
        assertEquals(3, x);
        
        int y = reduce(add, ints, 0);
        assertEquals(15, y);   
        
        
        F2<Integer,Integer,Integer> addX2 = new F2<Integer,Integer,Integer>(){
            @Override
			public Integer invoke(Integer a0, Integer a1) {
                return a0 + a1;
            }   
        };
        
        int u = reduce(addX2, ints, 0);
        assertEquals(15,u);
    }
    
    public void testCurryWithLambda(){
        Lambda<Integer,Integer> succ = curry(add, 1);
        assertEquals(9, (int)succ.x(8));
    }

    public void testCurryWithFunction() {
        F2<Integer,Integer,Integer> add = new F2<Integer,Integer,Integer>(){
			public Integer invoke(Integer a0, Integer a1) {
                return a0 + a1;
            }   
        };
        
        Lambda<Integer,Integer> succ = curry(add,1);
        
        int twenty1 = add.x(succ.x(10), succ.x(9));
        assertEquals(21, twenty1);
    }
    
    
    public void testLambdaF2() {
        F2<Integer,Integer,Integer> f0 = new F2<Integer,Integer,Integer>(){
			public Integer invoke(Integer a0, Integer a1) {
                return a0 + a1;
            }           
        };
        
        // first we get a result using chaining
        int five = f0.x(2).x(3);
        assertEquals(5,five);
        
        // then we use the two argument version
        int seven = f0.x(5,2);
        assertEquals(7,seven);             
    } 

    public void testLambdaF3() {
        F3<Integer,Integer,Integer,Integer> f = new F3<Integer, Integer, Integer, Integer>(){
			public Integer invoke(Integer a0, Integer a1, Integer a2) {
                return a0 + a1 + a2;
            }       
        };
        
        // check chaining
        int six = f.x(2).x(1).x(3);
        assertEquals(6, six);
               
        int seven = f.x(3,5,-1);
        assertEquals(7, seven);
    }
      
}